package com.wei.czz.framework.admin.manager;

import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.constant.EntityConstant;
import com.wei.czz.common.constant.RedisConstant;
import com.wei.czz.common.dto.admin.userRisk.UserRiskDto;
import com.wei.czz.common.dto.admin.userRisk.UserRiskEnumDto;
import com.wei.czz.common.dto.admin.userRisk.UserRiskFlowDto;
import com.wei.czz.common.dto.common.NameValue;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.admin.user.UserAppealEnum;
import com.wei.czz.common.enums.admin.user.UserRiskStatusEnum;
import com.wei.czz.common.enums.admin.user.UserRiskTypeEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.tool.Result;
import com.wei.czz.common.utils.CacheKeyUtils;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.vo.admin.AuditUserRiskVo;
import com.wei.czz.common.vo.admin.UserAppealVo;
import com.wei.czz.common.vo.admin.UserRiskVo;
import com.wei.czz.common.vo.admin.userRisk.UserRiskFormVo;
import com.wei.czz.framework.admin.entity.UserAppealEntity;
import com.wei.czz.framework.admin.entity.UserEntity;
import com.wei.czz.framework.admin.entity.UserRiskFlowEntity;
import com.wei.czz.framework.admin.helper.UserHelper;
import com.wei.czz.framework.admin.service.UserAppealService;
import com.wei.czz.framework.admin.service.UserRiskFlowService;
import com.wei.czz.framework.admin.service.UserService;
import com.wei.czz.framework.common.helper.CacheHelper;
import com.wei.czz.framework.common.handler.redis.RedisHandler;
import com.wei.czz.framework.common.service.AsyncService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-12-08 10:36:29
 * className: UserRiskManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class UserRiskManager {

    private static final Logger log = LoggerFactory.getLogger(UserRiskManager.class);

    private final UserService userService;

    private final UserRiskFlowService userRiskFlowService;

    private final UserAppealService userAppealService;

    private final RedissonClient redissonClient;
    
    private final AsyncService asyncService;

    private final UserHelper userHelper;

    private final RedisHandler redisHandler;

    private final CacheHelper cacheHelper;

    @Transactional
    public void saveUserRisk(UserRiskVo userRiskVo) {
        // 获取正在保存用户风控信息的用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        if (userPo.getUserId().equals(userRiskVo.getUserId())) {
            // 如果操作用户和被禁用用户一样
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "无法禁用自己，请确认。");
        }

        // 获取Redisson分布式锁对象
        RLock lock = redissonClient.getLock(RedisConstant.LOCK + EntityConstant.USER_RISK + RedisConstant.SPLIT
                + userRiskVo.getUserId());
        // 尝试加锁30秒
        boolean bool = lock.tryLock();
        if (!bool) {
            log.info("尝试加锁失败。lockKey={}", lock.getName());
            throw new CzzException(ResultEnum.TRY_LOCK_FAIL.getCode(), "禁止用户操作失败，其他用户正在对该用户进行风控操作");
        }
        try {

            /*
                获取用户风控记录
             */
            List<UserRiskFlowEntity> userRiskFlowList = userRiskFlowService.findUserRiskFlowList(userRiskVo.getUserId());
            bool = userRiskFlowList.stream().anyMatch(userRiskFlow ->
                    userRiskFlow.getStatus().compareTo(UserRiskStatusEnum.FINISH_STATUS.getValue()) < 0
            );
            if (bool) {
                // 用户有风控正在走流程时，不允许在增加用户风险记录
                throw new CzzException(ResultEnum.REFRESH.getCode(), "用户已被禁止，无需重复禁止。");
            }

            /*
                修改用户
             */
            UserEntity updateUser = new UserEntity();
            updateUser.setUserId(userRiskVo.getUserId())
                .setStatus(CommonEnum.ONE.getValue())
                .setUpdateTime(date)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getUserId());
            // 更新用户信息
            userService.saveOrEdit(updateUser);

            /*
                添加用户风控记录
             */
            UserRiskFlowEntity userRiskFlow = new UserRiskFlowEntity();
            // 设置用户id、风控类型（’管理员操作‘）、风控原因、风控状态（’未申诉‘）、更新时间、操作更新的用户id、创建时间和操作创建的用户id属性值
            userRiskFlow.setUserId(userRiskVo.getUserId())
                    .setRiskType(userRiskVo.getRiskType())
                    .setRiskReason(userRiskVo.getWord())
                    .setStatus(UserRiskStatusEnum.NONE_STATUS.getValue())
                    .setDeleteStatus(CommonEnum.ZERO.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateTime(date)
                    .setCreateUser(userPo.getUsername())
                    .setCreateUserId(userPo.getUserId());
            // 操作数据库用户风控表，新增一条用户风控信息
            userRiskFlowService.saveOrEdit(userRiskFlow);

            // 删除用户缓存
            this.deleteUserCache();

        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    public Result<Boolean> saveAppeal(UserAppealVo userAppealVo) {

        /*
            获取用户风控记录
         */
        UserRiskFlowEntity userRiskFlow = userRiskFlowService.get(userAppealVo.getRiskRecordId());
        if (UserRiskStatusEnum.FINISH_STATUS.getValue().equals(userRiskFlow.getStatus())) {
            // 用户申诉完成
            return Result.getResult(ResultEnum.SUCCESS.getCode(), "您前面提交的申诉已通过，可以正常使用帐号了。",
                    true);
        } else if (UserRiskStatusEnum.CANCEL_STATUS.getValue().equals(userRiskFlow.getStatus())) {
            // 用户已解除禁用
            return Result.getResult(ResultEnum.SUCCESS.getCode(), "您已被解除禁用，可以正常使用帐号了。", true);
        }

        /*
            获取用户名称
         */
        String userName = userHelper.getUsername(userRiskFlow.getUserId());

        UserAppealEntity userAppeal = userAppealService.getLastUserAppeal(userAppealVo.getRiskRecordId());
        if (Objects.nonNull(userAppeal)) {
            if (userAppeal.getStatus().equals(UserAppealEnum.DO_STATUS.getValue())) {
                // 上一次申诉还在等待审核
                return Result.getResult(ResultEnum.REQUEST_FAIL.getCode(),
                        "您已经有申诉记录在审核中，可在’申诉记录‘中查看。");
            } else if (userAppeal.getStatus().equals(UserAppealEnum.PASS_STATUS.getValue())) {
                // 上一次申诉已经审核通过
                return Result.getResult(ResultEnum.SUCCESS.getCode(), "恭喜您，您的申诉已通过。", true);
            }
        }

        Date date = new Date();

        userAppeal = new UserAppealEntity();
        // 设置风控主键、申诉描述、申述状态（’正在申诉‘）、审核回复、审核时间、操作审核的用户id和创建时间属性值
        userAppeal.setRiskFlowId(userAppealVo.getRiskRecordId())
                .setUserId(userRiskFlow.getUserId())
                .setAppealDesc(userAppealVo.getAppealDesc())
                .setStatus(UserAppealEnum.DO_STATUS.getValue())
                .setAuditDesc(StringUtils.EMPTY)
                .setAuditTime(date)
                .setAuditUser(StringUtils.EMPTY)
                .setAuditUserId(CommonEnum.ZERO.getLongValue())
                .setDeleteStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                .setCreateTime(date)
                .setCreateUser(userName)
                .setCreateUserId(userRiskFlow.getUserId());
        // 操作数据库用户申诉记录表，保存用户申诉信息
        userAppealService.saveOrEdit(userAppeal);

        if (UserRiskStatusEnum.NONE_STATUS.getValue().equals(userRiskFlow.getStatus())) {
            /*
                如果用户风控记录状态为‘未申诉’，则将状态变更为‘正在申诉’
             */

            userRiskFlow = new UserRiskFlowEntity();
            userRiskFlow.setId(userAppealVo.getRiskRecordId())
                    .setStatus(UserRiskStatusEnum.DO_STATUS.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(userName)
                    .setUpdateUserId(userRiskFlow.getUserId());
            userRiskFlowService.saveOrEdit(userRiskFlow);
        }

        return Result.success(false);
    }

    public PageDto<UserRiskFlowDto> getUserRiskPageList(UserRiskFormVo userRiskFormVo) {

        /*
            缓存请求参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.USER_RISK);
        cacheHelper.cache(cacheKey, userRiskFormVo);

        /*
            分页获取用户风控记录
         */
        PageDto<UserRiskFlowEntity> pageDto = userRiskFlowService.getPageList(userRiskFormVo);
        if (pageDto.isEmpty()) {
            log.info("分页查询用户风控记录为空");
            return pageDto.rebuild();
        }

        List<UserRiskFlowEntity> userRiskFlowList = pageDto.getList();

        /*
            获取用户名称映射
         */
        Set<Long> userIdSet = CopyUtils.mapSet(userRiskFlowList, UserRiskFlowEntity::getUserId);
        Map<Long, String> userNameMap = userService.getNameMap(userIdSet, Function.identity());

        /*
            封装返回
         */
        List<UserRiskFlowDto> userRiskFlowDtoList = CopyUtils.mapList(userRiskFlowList, userRiskFlow -> {
            UserRiskFlowDto userRiskFlowDto = CopyUtils.map(userRiskFlow, UserRiskFlowDto.class);
            userRiskFlowDto.setUserName(userNameMap.get(userRiskFlow.getUserId()))
                    .setRiskTypeName(UserRiskTypeEnum.getName(userRiskFlow.getRiskType()))
                    .setStatusName(UserRiskStatusEnum.getName(userRiskFlow.getStatus()));
            return userRiskFlowDto;
        });

        // 数据封装返回
        return pageDto.rebuild(userRiskFlowDtoList);
    }

    public UserRiskDto getLastRisk(Long userId) {

        /*
            获取用户风控记录
         */
        List<UserRiskFlowEntity> userRiskFlowList = userRiskFlowService.findUserRiskFlowList(userId);
        if (userRiskFlowList.isEmpty()) {
            throw new CzzException(ResultEnum.REFRESH.getCode(), "用户已没有风控记录，请确认后重新操作");
        }
        UserRiskFlowEntity userRiskFlow = userRiskFlowList.get(userRiskFlowList.size() - 1);
        log.info("用户最新一条风控记录：{}", userRiskFlow);

        UserRiskDto userRiskDto = CopyUtils.map(userRiskFlow, UserRiskDto.class);
        userRiskDto.setRiskTypeName(UserRiskTypeEnum.getName(userRiskFlow.getRiskType()));
        return userRiskDto;
    }

    public UserRiskEnumDto getEnums() {
        // 获取请求用户主键
        /*
            获取请求参数
         */
        String cacheKey = CacheKeyUtils.getParamCacheKey(EntityConstant.USER_RISK);
        UserRiskFormVo userRiskFormVo = cacheHelper.get(cacheKey, UserRiskFormVo.class);

        /*
            获取被风控过的用户主键集合
         */
        Set<Long> userIdSet = userRiskFlowService.getRiskUserIdSet();

        /*
            获取用户名称映射
         */
        Map<Long, String> userNameMap = userService.getNameMap(userIdSet, Function.identity());

        List<NameValue<String>> userList = new ArrayList<>(userIdSet.size());
        for (Long userId : userIdSet) {
            userList.add(new NameValue<>(userNameMap.get(userId), userId.toString()));
        }

        UserRiskEnumDto userRiskEnumDto = new UserRiskEnumDto();
        userRiskEnumDto.setUserList(userList)
                .setTypeList(UserRiskTypeEnum.getList())
                .setStatusList(UserRiskStatusEnum.getList())
                .setUserRiskFormVo(userRiskFormVo);
        return userRiskEnumDto;
    }

    public void checkUserRisk(Long id) {
        /*
            获取用户风控记录
         */
        UserRiskFlowEntity userRiskFlow = userRiskFlowService.get(id);
        if (UserRiskStatusEnum.NONE_STATUS.getValue().equals(userRiskFlow.getStatus())) {
            throw new CzzException(ResultEnum.REFRESH.getCode(), "用户尚未提交申诉，不可进行审核操作。");
        }
        if (UserRiskStatusEnum.FINISH_STATUS.getValue().equals(userRiskFlow.getStatus())) {
            throw new CzzException(ResultEnum.REFRESH.getCode(), "用户风控流程已结束，无需再审核。");
        }
        if (UserRiskStatusEnum.CANCEL_STATUS.getValue().equals(userRiskFlow.getStatus())) {
            throw new CzzException(ResultEnum.REFRESH.getCode(), "用户风控流程已取消，无需再审核。");
        }

        /*
            获取用户申诉记录
         */
        UserAppealEntity userAppeal = userAppealService.findLastOne(id);
        if (Objects.isNull(userAppeal)) {
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "用户申诉记录不存在，请确认。");
        }
        if (UserAppealEnum.PASS_STATUS.getValue().equals(userAppeal.getStatus())) {
            throw new CzzException(ResultEnum.REFRESH.getCode(), "用户申诉已被审核通过，无需再进行审核操作");
        }
        if (UserAppealEnum.REJECT_STATUS.getValue().equals(userAppeal.getStatus())) {
            throw new CzzException(ResultEnum.REFRESH.getCode(), "用户申诉已被审核拒绝，无需再进行审核操作");
        }
    }

    public void cancelUserRisk(Long id, Long userId, String remark) {
        // 获取正在更新用户风控信息的用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        String lockKey = RedisConstant.LOCK + EntityConstant.USER_RISK + RedisConstant.SPLIT + userId;
        // 获取Redisson分布式锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 尝试加锁30秒
        boolean bool = lock.tryLock();
        log.info("尝试加锁结果。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            throw new CzzException(ResultEnum.TRY_LOCK_FAIL.getCode(), "用户解禁操作失败，其他用户正在对该用户进行风控操作");
        }
        try {
            /*
                获取用户风控记录
             */
            UserRiskFlowEntity userRiskFlow = userRiskFlowService.get(id);
            // 获取用户风控状态
            Integer status = userRiskFlow.getStatus();
            log.info("用户风控状态。status={}", status);
            if (status.compareTo(UserRiskStatusEnum.DO_STATUS.getValue()) > 0) {
                // 用户已经走完风控流程时，不允许在修改用户风险记录
                throw new CzzException(ResultEnum.REFRESH.getCode(), "用户已不是禁止状态，解禁操作失败。");
            }

            /*
                修改用户
             */
            UserEntity updateUser = new UserEntity();
            updateUser.setUserId(userId)
                .setStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(userPo.getUsername())
                .setUpdateUserId(userPo.getUserId());
            // 更新用户信息
            userService.saveOrEdit(updateUser);

            UserRiskFlowEntity updateUserRiskFlow = new UserRiskFlowEntity();
            // 设置风控记录主键、风控状态（’取消风控‘）、更新时间、操作更新的用户和备注属性值
            updateUserRiskFlow.setId(id)
                    .setStatus(UserRiskStatusEnum.CANCEL_STATUS.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(userPo.getUsername())
                    .setUpdateUserId(userPo.getUserId())
                    .setRemark(remark);
            // 操作数据库用户风控表，更新用户风控信息
            userRiskFlowService.saveOrEdit(updateUserRiskFlow);

            // 删除用户缓存
            this.deleteUserCache();

            // 异步清理用户申请记录
            asyncService.execute(() -> {
                userAppealService.deleteAppeal(id);
            });

        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    @Transactional
    public void auditUserRisk(AuditUserRiskVo auditUserRiskVo) {
        // 获取正在审核用户申诉记录的用户
        UserPo userPo = SecurityUtils.getUser();
        Date date = new Date();

        String lockKey = RedisConstant.LOCK + EntityConstant.USER_RISK + RedisConstant.SPLIT +
                auditUserRiskVo.getUserId();
        // 获取Redisson分布式锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 尝试加锁30秒
        boolean bool = lock.tryLock();
        log.info("尝试加锁完成。lockKey={} bool={}", lockKey, bool);
        if (!bool) {
            throw new CzzException(ResultEnum.TRY_LOCK_FAIL.getCode(), "用户申诉审核提交失败，其他用户正在对该用户进行风控操作");
        }
        try {
            /*
                获取用户风控记录
             */
            UserRiskFlowEntity userRiskFlow = userRiskFlowService.get(auditUserRiskVo.getRiskFlowId());

            // 查询数据库用户风控记录表，获取用户风控状态
            Integer status = userRiskFlow.getStatus();
            log.info("用户风控记录状态。status={}", status);
            if (!UserRiskStatusEnum.DO_STATUS.getValue().equals(status)) {
                throw new CzzException(ResultEnum.REFRESH.getCode(), 
                        "风控记录已不是申诉状态，提交失败，请确认，如有问题，请联系管理员处理。");
            }

            /*
                更新用户风控申请
             */
            UserAppealEntity userAppeal = new UserAppealEntity();
            userAppeal.setId(auditUserRiskVo.getAppealId())
                    .setStatus(auditUserRiskVo.getAuditStatus())
                    .setAuditDesc(auditUserRiskVo.getAuditDesc())
                    .setAuditTime(date)
                    .setAuditUser(userPo.getUsername())
                    .setAuditUserId(userPo.getUserId());
            // 更新用户申诉记录数据
            userAppealService.updateUserAppeal(userAppeal);

            if (UserAppealEnum.PASS_STATUS.getValue().equals(auditUserRiskVo.getAuditStatus())) {
                /*
                    更新用户风控记录
                 */
                userRiskFlow = new UserRiskFlowEntity();
                userRiskFlow.setId(auditUserRiskVo.getRiskFlowId())
                        .setStatus(UserRiskStatusEnum.FINISH_STATUS.getValue())
                        .setUpdateTime(date)
                        .setUpdateUser(userPo.getUsername())
                        .setUpdateUserId(userPo.getUserId());
                // 操作数据库用户风控记录表，更新用户风控记录数据
                userRiskFlowService.saveOrEdit(userRiskFlow);

                /*
                    判断用户是否还存在风控记录，如果不存在了，则解除用户风控状态
                 */
                List<UserRiskFlowEntity> userRiskFlowList =
                        userRiskFlowService.findUserRiskFlowList(auditUserRiskVo.getUserId());
                long count = userRiskFlowList.stream().filter(userRisk -> {
                    if (UserRiskStatusEnum.NONE_STATUS.getValue().equals(userRisk.getStatus())) {
                        return true;
                    }
                    return UserRiskStatusEnum.DO_STATUS.getValue().equals(userRisk.getStatus());
                }).count();

                if (count == 0) {
                    log.info("解除用户风控状态");
                    UserEntity updateUser = new UserEntity();
                    updateUser.setUserId(auditUserRiskVo.getUserId())
                        .setStatus(CommonEnum.ZERO.getValue())
                        .setUpdateTime(date)
                        .setUpdateUser(userPo.getUsername())
                        .setUpdateUserId(userPo.getUserId());
                    // 更新用户数据
                    userService.saveOrEdit(updateUser);

                    // 删除用户缓存
                    this.deleteUserCache();
                }
            }
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }
    }

    /**
     * 删除所有用户相关的临时缓存
     */
    private void deleteUserCache() {
        String[] patternKeys = new String[] {
                RedisConstant.STRING + EntityConstant.USER + Constant.UNDER + RedisConstant.LIST
                        + RedisConstant.ASTERISK
        };
        redisHandler.fuzzyDelete(patternKeys);
    }

}
